
#include <BFont>
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BSystem>
#include <BSplitItem>

#include "member_BSplitButton.h"
#include "member_BSplitItem.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BSplitButton*)_ptr)
#define member_allocate()		_ptr = new member_BSplitButton(this)
#define member_release()		delete (member_BSplitButton*)_ptr

BSplitButton::BSplitButton(BWidget* parent) : BButton(parent)
{
	member_allocate();
}
BSplitButton::BSplitButton(Form form, BWidget* parent) : BButton(form, parent)
{
	member_allocate();
}
BSplitButton::BSplitButton(const BString& text, BWidget* parent) : BButton(text, parent)
{
	member_allocate();
}
BSplitButton::~BSplitButton()
{
	member_release();
}

void BSplitButton::setExclusive(bool exclusive)
{
	if (member.exclusive != exclusive)
	{
		member.exclusive = exclusive;
		if (member.exclusive && member.items.size() && !member.current)
		{
			setCurrent(member.items[0]);
		}
	}
}
bool BSplitButton::exclusive() const
{
	return member.exclusive;
}

void BSplitButton::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		member.dirty = true;
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
	}
}
bool BSplitButton::readOnly() const
{
	return member.readOnly;
}

void BSplitButton::setArrowSize(const BSize& arrowSize)
{
	if (member.arrowSize != arrowSize)
	{
		member.arrowSize = arrowSize;
		this->fresh();
	}
}
const BSize& BSplitButton::arrowSize() const
{
	return member.arrowSize;
}

void BSplitButton::setArrowPart(Part arrowPart)
{
	if (member.arrowPart != arrowPart)
	{
		member.arrowPart = arrowPart;
		this->fresh();
	}
}
Part BSplitButton::arrowPart() const
{
	return member.arrowPart;
}

void BSplitButton::setArrowAlign(Align arrowAlign)
{
	if (member.arrowAlign != arrowAlign)
	{
		member.arrowAlign = arrowAlign;
		this->fresh();
	}
}
Align BSplitButton::arrowAlign() const
{
	return member.arrowAlign;
}

bool BSplitButton::setText(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->text() == text)
		{
			return setCurrent(item);
		}
	}
	return setText(text);
}

void BSplitButton::setValue(const BValue& value)
{
	if (member.value != value)
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			BSplitItem* item = member.items[i];
			if (item->value() == value)
			{
				setCurrent(item);
			}
		}
	}
}
const BValue& BSplitButton::value() const
{
	return member.value;
}

bool BSplitButton::addItem(BSplitItem* item)
{
	if (!item)
		return false;
	if (item_member(item)->splitButton == this)
		return true;
	if (!member.items.append(item))
		return false;
	if (item_member(item)->splitButton)
	{
		item_member(item)->splitButton->removeItem(item);
	}
	item_member(item)->splitButton = this;
	member.connect(item, Signal_ColorChanged, &member_BSplitButton::slotItemColorChanged);
	member.connect(item, Signal_GraphChanged, &member_BSplitButton::slotItemGraphChanged);
	member.connect(item, Signal_ImageChanged, &member_BSplitButton::slotItemImageChanged);
	member.connect(item, Signal_IconChanged, &member_BSplitButton::slotItemIconChanged);
	member.connect(item, Signal_TextChanged, &member_BSplitButton::slotItemTextChanged);
	member.connect(item, Signal_ValueChanged, &member_BSplitButton::slotItemValueChanged);
	member.board->dirty = true;
	member.board->fresh();
	member.dirty = true;
	this->emit(Signal_ItemInserted, item);
	if (member.exclusive && member.items.size() && !member.current)
	{
		setCurrent(item);
	}
	this->adjust();
	return true;
}
BSplitItem* BSplitButton::addItem(const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(text, value);
	addItem(item);
	return item;
}
BSplitItem* BSplitButton::addItem(Graph graph, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(graph, text, value);
	addItem(item);
	return item;
}
BSplitItem* BSplitButton::addItem(BIcon* icon, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(icon, text, value);
	addItem(item);
	return item;
}
BSplitItem* BSplitButton::addItem(BImage* image, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(image, text, value);
	addItem(item);
	return item;
}

bool BSplitButton::insertItem(int pos, BSplitItem* item)
{
	if (!item)
		return false;
	if (item_member(item)->splitButton == this)
	{
		int index = member.board->items->find(item);
		member.board->items->move(index, pos);
		return true;
	}
	if (!member.items.insert(pos, item))
		return false;
	if (item_member(item)->splitButton)
	{
		item_member(item)->splitButton->removeItem(item);
	}
	item_member(item)->splitButton = this;
	member.connect(item, Signal_ColorChanged, &member_BSplitButton::slotItemColorChanged);
	member.connect(item, Signal_GraphChanged, &member_BSplitButton::slotItemGraphChanged);
	member.connect(item, Signal_ImageChanged, &member_BSplitButton::slotItemImageChanged);
	member.connect(item, Signal_IconChanged, &member_BSplitButton::slotItemIconChanged);
	member.connect(item, Signal_TextChanged, &member_BSplitButton::slotItemTextChanged);
	member.connect(item, Signal_ValueChanged, &member_BSplitButton::slotItemValueChanged);
	member.board->dirty = true;
	member.board->fresh();
	member.dirty = true;
	this->emit(Signal_ItemInserted, item);
	if (member.exclusive && member.items.size() && !member.current)
	{
		setCurrent(item);
	}
	this->adjust();
	return true;
}
BSplitItem* BSplitButton::insertItem(int pos, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(text, value);
	insertItem(pos, item);
	return item;
}
BSplitItem* BSplitButton::insertItem(int pos, Graph graph, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(graph, text, value);
	insertItem(pos, item);
	return item;
}
BSplitItem* BSplitButton::insertItem(int pos, BIcon* icon, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(icon, text, value);
	insertItem(pos, item);
	return item;
}
BSplitItem* BSplitButton::insertItem(int pos, BImage* image, const BString& text, const BValue& value)
{
	BSplitItem* item = new BSplitItem(image, text, value);
	insertItem(pos, item);
	return item;
}

bool BSplitButton::removeItem(BSplitItem* item)
{
	if (!item || item_member(item)->splitButton != this)
		return false;
	item_member(item)->splitButton = 0;
	BHolder<BSplitItem> holder(item);
	member.items.remove(item);
	member.disconnect(item);
	member.board->hoveredItem = 0;
	member.board->pressedItem = 0;
	member.board->dirty = true;
	member.board->fresh();
	if (member.current == item)
	{
		item = member.exclusive ? member.items.first() : 0;
		setCurrent(item);
	}
	this->emit(Signal_ItemRemoved, item);
	return true;
}
bool BSplitButton::removeItem(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->text() == text)
		{
			return removeItem(item);
		}
	}
	return false;
}
bool BSplitButton::removeItem(int index)
{
	BSplitItem* item = member.items(index);
	return removeItem(item);
}
void BSplitButton::clearItems()
{
	if (member.items.size())
	{
		while (BSplitItem* item = member.items.last())
		{
			BHolder<BSplitItem> holder(item);
			member.items.pop();
			member.disconnect(item);
			this->emit(Signal_ItemRemoved, item);
		}
		member.items.clear();
		member.board->dirty = true;
		member.board->fresh();
		member.dirty = true;
		if (member.current)
			setCurrent(0);
		this->fresh();
	}
}

bool BSplitButton::containItem(const BSplitItem* item) const
{
	return member.items.contain(item);
}
bool BSplitButton::containItem(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->value() == value)
			return true;
	}
	return false;
}
bool BSplitButton::containItem(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->text() == text)
			return true;
	}
	return false;
}

int BSplitButton::itemIndex(const BSplitItem* item) const
{
	return member.items.find(item);
}
int BSplitButton::itemIndex(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->value() == value)
			return i;
	}
	return -1;
}
int BSplitButton::itemIndex(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->text() == text)
			return i;
	}
	return -1;
}

int BSplitButton::itemCount() const
{
	return member.items.size();
}
BSplitItem* BSplitButton::item(int index)
{
	return member.items(index);
}
const BSplitItem* BSplitButton::item(int index) const
{
	return member.items(index);
}

BSplitItem* BSplitButton::item(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->text() == text)
			return item;
	}
	return 0;
}
const BSplitItem* BSplitButton::item(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BSplitItem* item = member.items[i];
		if (item->text() == text)
			return item;
	}
	return 0;
}

BSplitItem* BSplitButton::itemOfValue(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		if (item->value() == value)
			return item;
	}
	return 0;
}
const BSplitItem* BSplitButton::itemOfValue(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BSplitItem* item = member.items[i];
		if (item->value() == value)
			return item;
	}
	return 0;
}

bool BSplitButton::moveItem(const BSplitItem* item, int pos)
{
	int index = member.items.find(item);
	if (member.items.move(index, pos))
	{
		int min = bMin(index, pos);
		int max = bMax(index, pos);
		member.board->fresh();
		return true;
	}
	return false;
}
bool BSplitButton::moveItem(const BString& text, int pos)
{
	BSplitItem* item = this->item(text);
	return moveItem(item, pos);
}
bool BSplitButton::moveItem(const BValue& value, int pos)
{
	BSplitItem* item = itemOfValue(value);
	return moveItem(item, pos);
}

bool BSplitButton::setCurrent(BSplitItem* item)
{
	if (item && item_member(item)->splitButton != this)
		return false;
	if (!item && member.exclusive)
		return false;
	if (member.current != item)
	{
		member.current = item;
		member.value.clear();
		if (member.current)
		{
			member.value = item->value();
		}
		member.board->dirty = true;
		member.board->fresh();
		member.dirty = true;
		if (this->checks(Signal_TextChanged))
			emit(Signal_TextChanged, this->text());
		if (this->checks(Signal_ValueChanged))
			emit(Signal_ValueChanged, member.value);
		if (this->checks(Signal_CurrentChanged))
			emit(Signal_CurrentChanged, member.current);
		this->fresh();
		return true;
	}
	return false;
}
const BSplitItem* BSplitButton::current() const
{
	return member.current;
}
BSplitItem* BSplitButton::current()
{
	return member.current;
}

bool BSplitButton::setCurrentIndex(int index)
{
	BSplitItem* item = member.items(index);
	return setCurrent(item);
}
int BSplitButton::currentIndex() const
{
	if (member.current)
		return member.current->index();
	return -1;
}

void BSplitButton::setMaxShowCount(int maxShowCount)
{
	if (member.board->maxShowCount != maxShowCount)
	{
		member.board->maxShowCount = maxShowCount;
		member.board->dirty = true;
		member.board->fresh();
	}
}
int BSplitButton::maxShowCount() const
{
	return member.board->maxShowCount;
}

void BSplitButton::showBoard()
{
	member.arrow->setChecked(true);
}
void BSplitButton::hideBoard()
{
	member.arrow->setChecked(false);
}

bool BSplitButton::query(const BString& text, BValue& value) const
{
	if (text == "board")
	{
		value = member.board;
		return true;
	}
	if (text == "arrow")
	{
		value = member.arrow;
		return true;
	}
	return BButton::query(text, value);
}

void BSplitButton::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BButton::resizeEvent(event);
}

void BSplitButton::styleEvent(const BEvent& event)
{
	BButton::styleEvent(event);
	const BStyle* style = event.value();

	if (const BFont* font = this->realFont())
	{
		member.arrow->setIconSize(font->size() / 2);
	}

	style_member(style)->assign(Value_Arrow_Size, member.arrowSize);
	style_member(style)->assign(Value_Arrow_Part, member.arrowPart);
	style_member(style)->assign(Value_Arrow_Align, member.arrowAlign);

	for (int i = 0; i < member.items.size(); i++)
	{
		BSplitItem* item = member.items[i];
		item_member(item)->dirtyStyle = true;
	}
	member.board->dirty = true;
	member.dirty = true;
}
void BSplitButton::freshEvent(const BEvent& event)
{
	if (member.board->dirty)
	{
		member.board->freshItemRects();
		member.board->dirty = false;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BButton::freshEvent(event);
}

void BSplitButton::activeEvent(const BEvent& event)
{
	if (event.value() == false)
	{
		member.arrow->setChecked(false);
	}
	BButton::activeEvent(event);
}
